#ifndef PROCESS_H
#define PROCESS_H

#include <Globals.h>
#include <MemoryManagement.h>
#include <Timer.h>
#include <DynamicLinker.h>
#include <ElfParser.h>
#include <LinkedList.h>
#include <InterruptServiceRoutine.h>
#include <SerialDriver.h>
#include <Map.h>
#include <Resource.h>
#include <RemoteResource.h>
#include <MemoryMap.h>

#define NUM_SIGNALS 64

/**
  Syscall interface.
**/
namespace Process_IF
{
  const int execve               =0;
  const int createFileDescriptor =1;
  const int destroyFileDescriptor=2;
  const int getFileDescriptor    =3;
  const int getPid               =4;
  const int sbrk                 =5;
  const int waitPid              =6;
  const int setErrnoAddress      =7;
  const int setProcessPtrAddress =8;
  const int exit                 =9;
  const int getMessageSender     =10;
  const int getCtty              =11;
  const int initialiseSignals    =12;
  const int setCtty              =13;
}

class FileDescriptor;

/**
  Implements a process - that is, a thread of execution which resides in its own
  virtual memory space.
**/
class Process
{
friend class ProcessManager;
public:
  /**
    A list of strings.
  **/
  typedef LinkedList<char *> StringList;
  
  /**
    Main constructor. Creates a process object.
  **/
  typedef Map<int,void*,32> RpcHandlers;
  Process(PageDirectory *pageDirectory, Address kstack, Address brk,
          Elf32Parser *parser=NULL, DynamicLinker *linker=NULL, RemoteResource *_resource=NULL,
          MemoryMap *_memoryMap=NULL,
          Process *parent=NULL);

  ~Process();

  /**
     Create a new process running in a different address space
     but running the same code.
  **/
  u32int fork();
  
  /**
    Causes this process to stop running indefinately.
    Assumes: called via an interrupt, not via sysenter.
             this process is currently running.
  **/
  void exit(s32int val);
  
  /**
     Replace the process' virtual memory image with that of the program
     specified, to be run with the specified arguments and environment.
  **/
  void execve(char *prog, StringList argv, StringList env);
  void execve2(char *prog, char **argv, char **env);

  /**
     Return the next available file descriptor.
  **/
  u32int getNextFD()
  {
    return nextFD++;
  }
  
  /**
    General accessor functions
  **/
  Address getStackPointer() {return stackPointer;}
  Address getBasePointer()  {return basePointer;}
  Address getInstructionPointer() {return instructionPointer;}
  PageDirectory *getPageDirectory() {return pageDirectory;}
  u32int getPid() {return pid;}
  u32int getLastRun() {return lastRun;}
  bool isInKernel() {return inKernel;}
  Address getKernelStack() {return kernelStack;}
  void setStackPointer(Address a) {stackPointer = a;}
  void setBasePointer(Address a) {basePointer = a;}
  void setInstructionPointer(Address a) {instructionPointer = a;}
  void setPageDirectory(PageDirectory *p) {pageDirectory = p;}
  void setInKernel(bool b) {inKernel = b;}
  void updateLastRun() {lastRun = timer->getTick();}
  DynamicLinker *getLinker() {return linker;}
  Elf32Parser *getElfParser() {return parser;}
  void setErrno(u32int error) {errno=error;}
  u32int getErrno() {return errno;}
  void setCtty(class File *t);
  void setCttyName(char *name);
  
  u32int getMessageSender() {return messageSender;}
  class File *getCtty() {return ctty;}
  u32int getCttyResource();
  
  
  void initialiseSignals(u32int resourceId);
  
  /**
    Adds an RPC call to the end of the RPC queue.
  **/
  void queueRpcCall(class RpcCall *call);
  
  /**
    Returns true if the process is currently processing RPCs.
  **/
  bool isProcessingRpcs()
  {
    return processingRpcs.testLock();
  }

  /**
    Iterates through the RpcCall list and executes all calls it finds
    (then removes them from the list).
  **/
  void executeRpcCalls(class RpcCall *call);

  /**
    Called by the user-mode RPC handler when it needs more calls to execute.
  **/
  class RpcCall *moreRpc();

  /**
    Sends the process a signal.
  **/
  void sendSignal(u32int signal);
  
  /**
    Changes the memory breakpoint by incr.
  **/
  Address sbrk(s32int incr);
  
  /**
    Sets/gets the pointer to the register state for this process.
  **/
  void setRegistersPtr(Registers *r)
  {
    registers = r;
  }
  Registers *getRegistersPtr()
  {
    return registers;
  }
  
  void addFileDescriptor(FileDescriptor *fd);
  
  /**
    Make a new file descriptor, add it and return it's resource ID.
  **/
  u32int createFileDescriptor(char *filename, int mode, int flags);
  
  FileDescriptor *getFileDescriptor(int n);
  u32int getFileDescriptorResource(int n);
  
  FileDescriptor *deleteFileDescriptor(int n);
  
  /**
    Delete from both the process' map and the heap, not just the first.
  **/
  void destroyFileDescriptor(int n);
  
  /**
    Wait for a specified child process to end.
  **/
  int waitPid(int pid, int *stat_loc, int options);
  
  /**
    The status of this process.
  **/
  enum ProcessStatus
  {
    Running,
    Zombie
  };
  ProcessStatus status;
  
  /**
    The input/output privilege level for this process.
  **/
  u8int iopl;
  
  /**
    We are a system resource.
  **/
  Resource *resource;
  
  /**
    The memory map for this process.
  **/
  MemoryMap memoryMap;
  
private:
  
  /**
    The value given to the _exit call
  **/
  s32int exitValue;
  
  /**
    Process identifier
  **/
  u32int pid;
  
  /**
    Stack pointer, (EIP / RIP)
  **/
  Address stackPointer;
  
  /**
    Frame base pointer (EBP / RBP)
  **/
  Address basePointer;
  
  /**
    Instruction pointer (EIP / RIP)
  **/
  Address instructionPointer;
  
  /**
    Ring - RING_KERNEL, RING_SERVER, or RING_APP
  **/
  u32int ring;
  
  /**
    List of environment variables and values.
  **/
  char **environment;
  
  /**
    List of command-line arguments.
  **/
  char **arguments;
  
  /**
    Address of the kernel stack.
  **/
  Address kernelStack;
  
  /**
    Is the process currently in the kernel?
  **/
  bool inKernel;
  
  /**
    The address of the end of the .data section. Can be changed to
    facilitate a user-land heap.
  **/
  Address breakpoint;
  
  /**
    Virtual memory space.
  **/
  PageDirectory *pageDirectory;
  
  /**
    The time the process was last run
  **/
  u32int lastRun;
  
  /**
    The RPC queue.
  **/
  LinkedList<class RpcCall *> rpcQueue;
  
  /**
    The atomic spinlock to tell if we are currently processing RPC requests
  **/
  Lockable processingRpcs;
  
  /**
     The next available file descriptor
  **/
  u32int nextFD;
  
  /**
    The dynamic linker object for this process. It binds all shared libraries
    together.
  **/
  DynamicLinker *linker;

  /**
    The ELF file parser for this process. It maintains the symbol and string
    tables.
  **/
  Elf32Parser *parser;
  
  /**
    The 'errno' variable - this variable is set whenever an error
    in a kernel function occurs.
  **/
  u32int errno;
  
  /**
    The pointer to the register state for this process.
  **/
  Registers *registers;
  
  typedef Map<int, FileDescriptor*, 32> FileDescriptorMap;
  FileDescriptorMap filedescriptors;
  
  /**
    The children of this process, given as a Map.
  **/
  typedef Map<int, Process*, 32> ChildMap;
  ChildMap children;
  
  /**
    The parent process of this process.
  **/
  Process *parent;
  
  /**
    The controlling TTY of this process.
  **/
  class File *ctty;
  
  /**
    The sending PID of the currently executing RPC call.
  **/
  u32int messageSender;

  /**
    The resource of the signal handler, normally set in _premain.cc.
  **/
  RemoteResource *signalResource;
  
};

#endif
